/*
 * Circuit.cpp
 *
 * Created on: 05-05-2014
 *     Author: A.W. Janisse
 *
 * Version 1.0: 24-05-2014, Final release
 */

#include <iostream>
#include <cstdio>
#include <typeinfo>
#include <sstream>
#include <string.h>
#include <cstdlib>
#include "Circuit.h"
#include "Not.h"
#include "Low.h"
#include "High.h"
#include "Or.h"
#include "And.h"

using namespace std;

Circuit::Circuit()
{
    myGates = new vector<Gate *>();
}

Circuit::~Circuit()
{
    clearCircuit();
    delete myGates;
}

bool Circuit::addGate(int x, int y, string type)
{
    if(searchGate(x, y) != NULL)
        return false;

    if(type == "Not")
    {
        myGates->push_back( new Not(x, y, type) );
        return true;
    }
    else if(type == "Low")
    {
        myGates->push_back( new Low(x, y, type) );
        return true;
    }
    else if(type == "High")
    {
        myGates->push_back( new High(x, y, type) );
        return true;
    }
    else if(type == "Or")
    {
        myGates->push_back( new Or(x, y, type) );
        return true;
    }
    else if(type == "And")
    {
        myGates->push_back( new And(x, y, type) );
        return true;
    }

    return false;
}

bool Circuit::removeGate(int x, int y)
{
    for(unsigned int idx = 0; idx < myGates->size(); idx++)
    {
        int gx = myGates->at(idx)->getX();
        int gy = myGates->at(idx)->getY();

        if(x >= gx && x < gx + 8 && y >= gy && y < gy + 6)
        {
            // Find out if it's connected to a next gate.
            Gate *nextGate = getNextGate(myGates->at(idx));
            // If so, remove that entry.
            if(nextGate != NULL )
                nextGate->removeEntryTo(myGates->at(idx));
            // Deconstruct the gate.
            delete myGates->at(idx);
            // Remove the pointer from the list.
            myGates->erase(myGates->begin() + idx);
            return true;
        }
    }
    return false;
}

bool Circuit::addConnection(int x1, int y1, int x2, int y2)
{
    Gate *g1 = searchGate(x1, y1);
    Gate *g2 = searchGate(x2, y2);

    // Er zijn geen twee gates, return false.
    if(g1 == NULL or g2 == NULL)
        return false;

    // De gates zijn gelijk, return false.
    if(g1->getNumber() == g2->getNumber())
        return false;

    // x1 < x2, geen vrije ingang voor gate(x2,y2) of
    // geen vrije uitgang gate(x1,y2), return false.
    if(x1 < x2) {
        if(g2->getEntry(0) != NULL && g2->getEntry(1) != NULL)
            return false;

        if(getNextGate(g1) != NULL)
            return false;
    }
    // x2 < x1, geen vrije ingang voor gate(x1, y1) of
    // geen vrije uitgang gate(x2,y2), return false.
    if(x2 < x1) {
        if(g1->getEntry(0) == NULL || g1->getEntry(1) == NULL)
            return false;

        if(getNextGate(g2) != NULL)
            return false;
    }
    // x1 < x2, Maak connectie van een ingang gate(x2,y2) met gate(x1,y1), return true.
    if(x1 < x2) {
        g2->connectEntryTo(g1);
        return true;
    }
    // Maak connectie van een ingang gate(x1,y1) met gate(x2,y2), return true.
    g1->connectEntryTo(g2);
    return true;
}

bool Circuit::removeConnection(int x1, int y1, int x2, int y2)
{
    Gate *g1 = searchGate(x1, y1);
    Gate *g2 = searchGate(x2, y2);

    // Er zijn geen twee gates, return false.
    if(g1 == NULL or g2 == NULL)
        return false;

    // De gates zijn gelijk, return false.
    if(g1->getNumber() == g2->getNumber())
        return false;

    // Remove the connection and retrun true
    if (g1->getEntry(0) != NULL && g1->getEntry(0)->getNumber() == g2->getNumber()){
        g1->removeEntryTo(g2);
        return true;
    }
    else if (g1->getEntry(1) != NULL && g1->getEntry(1)->getNumber() == g2->getNumber()){
        g1->removeEntryTo(g2);
        return true;
    }
    else if (g2->getEntry(0) != NULL && g2->getEntry(0)->getNumber() == g1->getNumber()){
        g2->removeEntryTo(g1);
        return true;
    }
    else if (g2->getEntry(1) != NULL && g2->getEntry(1)->getNumber() == g1->getNumber()){
        g2->removeEntryTo(g1);
        return true;
    }
    else
        return false;
}

string Circuit::getDrawingInstruction()
{
    stringstream drawingString;

    for(unsigned int idx = 0; idx < myGates->size(); idx++)
    {
        drawingString << myGates->at(idx)->getDrawingInstruction();
    }
    return drawingString.str();
}

string Circuit::serialize()
{
    stringstream serialString;

    for(unsigned int idx = 0; idx < myGates->size(); idx++)
    {
        Gate *gate = myGates->at(idx);
        serialString << gate->serialize();
    }

    return serialString.str();
}

bool Circuit::deserialize(string serializedCircuit)
{
    vector<string> tokens;
    vector<string> instructions;
    string instr;
    string token;
    // Delete existing gates and connections
    clearCircuit();
    //Trim the string into instructions
    stringstream instrStream(serializedCircuit);
    while(getline(instrStream, instr, '\n'))
        instructions.push_back(instr);
    //Iterate over each instruction to find gates
    for(unsigned int idx = 0; idx < instructions.size(); idx++)
    {
        //Trim instruction into tokens
        stringstream tokenStream(instructions.at(idx));
        while(getline(tokenStream, token, ','))
            tokens.push_back(token);
        //If it is a gate than add it
        if(tokens.size() == 3){
            string gate = tokens.at(0);
            int x1 =  atoi(tokens.at(1).c_str());
            int y1 =  atoi(tokens.at(2).c_str());
            addGate(x1,y1,gate);
        }
        tokens.clear();
    }
    //Iterate over each instruction to find connections
    for(unsigned int idx = 0; idx < instructions.size(); idx++)
    {
        stringstream tokenStream(instructions.at(idx));
        while(getline(tokenStream, token, ','))
            tokens.push_back(token);
        //If it is a connection add it
        if (tokens.size() == 5){
            int x1 =  atoi(tokens.at(1).c_str());
            int y1 =  atoi(tokens.at(2).c_str());
            int x2 =  atoi(tokens.at(3).c_str());
            int y2 =  atoi(tokens.at(4).c_str());
            addConnection(x1,y1,x2,y2);
        }
        tokens.clear();
    }
    return true;
}

Gate *Circuit::searchGate(int x, int y)
{
    for(unsigned int idx = 0; idx < myGates->size(); idx++)
    {
        int gx = myGates->at(idx)->getX();
        int gy = myGates->at(idx)->getY();

        if(x >= gx && x < gx + 8 && y >= gy && y < gy + 6)
        {
            return myGates->at(idx);
        }
    }

    return NULL;
}

Gate * Circuit::getNextGate(Gate *gate)
{
    if(gate == NULL)
        throw "NULL reference error at getNextGate";

    Gate *entry0;
    Gate *entry1;

    for(unsigned int idx = 0; idx < myGates->size(); idx++)
    {
        entry0 = myGates->at(idx)->getEntry(0);
        entry1 = myGates->at(idx)->getEntry(1);

        if(entry0 != NULL && entry0->getNumber() == gate->getNumber())
            return myGates->at(idx);

        if(entry1 != NULL && entry1->getNumber() == gate->getNumber())
            return myGates->at(idx);
    }

    return NULL;
}

void Circuit::clearCircuit()
{
    // Delete any existing gates and connections
    for (unsigned int idx = 0; idx < myGates->size(); idx++ )
    {
        myGates->at(idx)->resetNextNumber();

        Gate *entry0 = myGates->at(idx)->getEntry(0);
        Gate *entry1 = myGates->at(idx)->getEntry(1);

        myGates->at(idx)->removeEntryTo(entry0);
        myGates->at(idx)->removeEntryTo(entry1);

        delete myGates->at(idx);
    }
    myGates->clear();
}
